package com.eagle.coders.common.ui.form.renderer;

import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;

import com.eagle.coders.common.bean.annotations.DomainTitle;
import com.eagle.coders.common.form.annotations.Action;
import com.eagle.coders.common.form.annotations.Actions;
import com.eagle.coders.common.form.annotations.DomainDisplayPolicies;
import com.eagle.coders.common.form.annotations.DomainDisplayPolicy;
import com.eagle.coders.common.form.annotations.DomainObject;
import com.eagle.coders.common.form.annotations.DomainObjects;
import com.eagle.coders.common.form.annotations.DomainProperties;
import com.eagle.coders.common.form.annotations.ExceptDomainObjectPropertyPolicy;
import com.eagle.coders.common.form.annotations.Group;
import com.eagle.coders.common.form.annotations.Groups;
import com.eagle.coders.common.form.annotations.MultipleExceptDomainObjectPropertyPolicies;
import com.eagle.coders.common.form.annotations.ResourceBundleFile;
import com.eagle.coders.common.form.annotations.type.DisplayOptionType;
import com.eagle.coders.common.ui.form.renderer.cache.UIDomainExceptPropertyBean;
import com.eagle.coders.common.ui.interfaces.IComponent;

/**
 * 
 * @author Anees
 * 
 */
public class FormRenderer {

	private static FormRenderer instance;

//	private Document document;
	private Element usecase;
	
	private List<UIDomainExceptPropertyBean> uiDomainPropertyExepHolderList;

	public FormRenderer() {
	}

	public static FormRenderer getInstance() {
		if (null == instance)
			instance = new FormRenderer();
		return instance;
	}

	/**
	 * 
	 * @param component
	 * @param isXML
	 */
	public void render(final IComponent component, Element root ,final boolean isXML) {

		if (!isXML){
//			document = DocumentFactory.getInstance().createDocument();
			usecase = root.addElement("usecase");
			usecase.addAttribute("id", component.getComponentIdentity());
			processFormAnnotation(component);
		}else
			component.getXml();
	}

	/**
	 * 
	 * @param component
	 */
	private void processFormAnnotation(final IComponent component) {
		processResourceKey(component); // To process the resource bundle
		processAllowedPropertiesFromDomainObject(component);
		processActionsAssociated(component);
		processDisplayOption(component);
		processDomainAnnotation(component);
	}
	
	/**
	 * 
	 * @param component
	 */
	private void processDisplayOption(final IComponent component){
		Element displayPoliciesElement = null;
		if(component.getClass().isAnnotationPresent(DomainDisplayPolicies.class)){
			displayPoliciesElement = usecase.addElement("displayPolicies");
			DomainDisplayPolicies displayPolicies = 
				component.getClass().getAnnotation(DomainDisplayPolicies.class);
			DisplayOptionType displayType = displayPolicies.displayOption();
			displayPoliciesElement.addAttribute("displayType", displayType.toString());
			DomainDisplayPolicy[] domainDisplayPolicyArray = 
				displayPolicies.displayPolicies();
			for(DomainDisplayPolicy domainDisplayPolicy : domainDisplayPolicyArray){
				processDisplayOption(domainDisplayPolicy, displayPoliciesElement);
			}
		}else if(component.getClass().isAnnotationPresent(DomainDisplayPolicy.class)){
			displayPoliciesElement = usecase.addElement("displayPolicies");
			displayPoliciesElement.addAttribute("displayType", DisplayOptionType.CASCADE.toString());
			DomainDisplayPolicy displayPolicy= 
				component.getClass().getAnnotation(DomainDisplayPolicy.class);
			processDisplayOption(displayPolicy, displayPoliciesElement);
		}else{
			displayPoliciesElement = usecase.addElement("displayPolicies");
			displayPoliciesElement.addAttribute("displayType", DisplayOptionType.CASCADE.toString());
		}
	}
	
	/**
	 * 
	 * @param displayPolicy
	 */
	private void processDisplayOption(final DomainDisplayPolicy displayPolicy, final Element displayElement){
		Element displayPolicyElement = displayElement.addElement("displayPolicy");
		
		String displayName = displayPolicy.displayName();
		displayPolicyElement.addAttribute("displayName", displayName);
		DisplayOptionType displayType = displayPolicy.displayOption();
		displayPolicyElement.addAttribute("displayType", displayType.toString());
		String displayPosition = displayPolicy.displayPosition();
		displayPolicyElement.addAttribute("displayPosition", displayPosition);
		String[] domainReferences= displayPolicy.domainReferences();
		for(String domainRef : domainReferences){
			Element domainRefElement = displayPolicyElement.addElement("domain-ref");
			domainRefElement.addText(domainRef);
		}
	}
	/**
	 * 
	 * @param component
	 */
	private void processDomainAnnotation(final IComponent component) {
		Element domainsElement = null;
		if (component.getClass().isAnnotationPresent(DomainObjects.class)) {
			domainsElement = usecase.addElement("domains");
			DomainObjects domainObjects = component.getClass().getAnnotation(
					DomainObjects.class);
			DomainObject domainObjectArray[] = domainObjects.domainObjects();
			for (DomainObject domainObject : domainObjectArray) {
				Element domainElement = domainsElement.addElement("domain");
				processDomainObject(domainObject, domainElement);
			}
		} else if (component.getClass().isAnnotationPresent(DomainObject.class)) {
			domainsElement = usecase.addElement("domains");
			Element domainElement = domainsElement.addElement("domain");
			DomainObject domainObject = component.getClass().getAnnotation(
					DomainObject.class);
			processDomainObject(domainObject, domainElement);
		}
	}

	/**
	 * 
	 * @param domainObject
	 */
	private void processDomainObject(DomainObject domainObject, Element domainElement) {
		String xml = domainObject.reference();
		String position = domainObject.position();
		Groups[] groups = domainObject.groups();
		domainElement.addAttribute("position", position);
		domainElement.addAttribute("ref", domainObject.domainObject().getName());
		
		processGroups(groups, domainElement);
		
		if (xml.equals("") || null != xml){
			xml = BeanRenderer.beanRenderer(domainObject, uiDomainPropertyExepHolderList, domainElement);
		}
		
//		System.out.println("[FormRenderer]{processFormAnnotation} :: list :: \n"+( uiDomainPropertyExepHolderList.get(0).getPropertyList()));
	}
	
	/**
	 * 
	 * @param groupsArray
	 */
	private void processGroups(Groups[] groupsArray, Element domainElement){
		
		for(Groups groups : groupsArray){
			Element groupsElement = domainElement.addElement("groups");
			
			String displayOption = groups.displayOption().toString();
			String position = groups.position();
			String name = groups.name();
				
			groupsElement.addAttribute("groups-display-type", displayOption);
			groupsElement.addAttribute("groups-position", position);
			groupsElement.addAttribute("groups-name", name);
				
			Group[] groupArray= groups.group();
			processGroup(groupArray, groupsElement);
		}
	}
	
	/**
	 * 
	 * @param groupArray
	 */
	private void processGroup(Group[] groupArray, Element groupsElement){
		for(Group group : groupArray){
			Element groupElement = groupsElement.addElement("group");
			String name = group.name();
			String groupPosition = group.position();
			String[] properties = group.properties();
			String displayOption = group.displayOption().toString();
			groupElement.addAttribute("group-name", name);
			groupElement.addAttribute("group-position", groupPosition);
			groupElement.addAttribute("group-display-type", displayOption);
			Element groupPropertiesElement= groupElement.addElement("group-properties");
			
			processProperties(properties, groupPropertiesElement);
		}
	}
	
	/**
	 * 
	 * @param properties
	 * @param groupPropertiesElement
	 */
	private void processProperties(String[] properties, Element groupPropertiesElement){
		for(String property : properties){
			Element domainPropertyElement = groupPropertiesElement.addElement("domain-property");
			domainPropertyElement.addAttribute("property-name", property);
		}
	}

	/**
	 * 
	 * @param component
	 */
	private void processActionsAssociated(final IComponent component) {
		Element actionsElement = null;
		if (component.getClass().isAnnotationPresent(Actions.class)) {
			actionsElement = usecase.addElement("actions");
			Actions actions = component.getClass().getAnnotation(Actions.class);
			Action[] actionArray = actions.actions();
			for (Action action : actionArray) {
				Element actionElement =actionsElement.addElement("action");
				processAction(action, actionElement);
			}
		} else if (component.getClass().isAnnotationPresent(Action.class)) {
			actionsElement = usecase.addElement("actions");
			Element actionElement = actionsElement.addElement("action");
			Action action = component.getClass().getAnnotation(Action.class);
			processAction(action, actionElement);
		}
	}

	/**
	 * 
	 * @param action
	 */
	private void processAction(Action action, Element actionElement) {
		String actionType = action.actionType().toString();
		String execution = action.exeution().getName();
		actionElement.addAttribute("actionType", actionType);
		actionElement.addAttribute("actionPerformer", execution);
		actionElement.addAttribute("performerType", action.performerType().toString());
		actionElement.addAttribute("performerPathID", action.performerPath());
	}
	
	/**
	 * 
	 * @param resourceBundleFile
	 */
	private void processResourceKey(final IComponent component){
		if(component.getClass().isAnnotationPresent(ResourceBundleFile.class)){
			ResourceBundleFile resourceBundleFile = component.getClass().getAnnotation(ResourceBundleFile.class);
			Element resourceBundleElement = usecase.addElement("resourceBundle");
			resourceBundleElement.addAttribute("fileName", resourceBundleFile.fileName());
			resourceBundleElement.addAttribute("fileLocation", resourceBundleFile.fileLocation());
		}
	}

	/**
	 * 
	 * @param component
	 */
	private void processAllowedPropertiesFromDomainObject(final IComponent component) {
		if (component.getClass().isAnnotationPresent(MultipleExceptDomainObjectPropertyPolicies.class)) {
			MultipleExceptDomainObjectPropertyPolicies multipleExceptDomainObjectPropertyPolicies =
				component.getClass().getAnnotation(MultipleExceptDomainObjectPropertyPolicies.class);

			ExceptDomainObjectPropertyPolicy exceptDomainObjectPropertyPolicy[] =
				multipleExceptDomainObjectPropertyPolicies.getValue();

			uiDomainPropertyExepHolderList =
				processExceptDomainObjectPropertyPolicyList(getExceptDomainObjectPolicy(exceptDomainObjectPropertyPolicy));

		} else if (component.getClass().isAnnotationPresent(ExceptDomainObjectPropertyPolicy.class)) {

			ExceptDomainObjectPropertyPolicy exceptDomainObjectPropertyPolicy =
				component.getClass().getAnnotation(ExceptDomainObjectPropertyPolicy.class);

			uiDomainPropertyExepHolderList = new ArrayList<UIDomainExceptPropertyBean>();

			uiDomainPropertyExepHolderList
					.add(processExceptDomainObjectPropertyPolicy(exceptDomainObjectPropertyPolicy));

		}
	}
	
	/**
	 * 
	 * @param exceptDomainObjectPropertyPolicies
	 * @return
	 */
	private List<ExceptDomainObjectPropertyPolicy> getExceptDomainObjectPolicy(
			ExceptDomainObjectPropertyPolicy exceptDomainObjectPropertyPolicies[]){
		List<ExceptDomainObjectPropertyPolicy> exceptDomainPropertyList =
			new ArrayList<ExceptDomainObjectPropertyPolicy>();
		for(ExceptDomainObjectPropertyPolicy exceptDomainObjectExptPolicy : exceptDomainObjectPropertyPolicies){
			exceptDomainPropertyList.add(exceptDomainObjectExptPolicy);
		}
		return exceptDomainPropertyList;
	}
	
	/**
	 * 
	 * @param exceptDomainPropertyList
	 */
	private List<UIDomainExceptPropertyBean> processExceptDomainObjectPropertyPolicyList(
			List<ExceptDomainObjectPropertyPolicy> exceptDomainPropertyList){
		List<UIDomainExceptPropertyBean> uiDomainPropertyExepHolderList = new ArrayList<UIDomainExceptPropertyBean>();
		for(ExceptDomainObjectPropertyPolicy exceptDoaminPropertyPlicy : exceptDomainPropertyList){
			uiDomainPropertyExepHolderList.add(processExceptDomainObjectPropertyPolicy(exceptDoaminPropertyPlicy));
		}
		return uiDomainPropertyExepHolderList;
	}
	
	/**
	 * 
	 * @param exceptDomainObjectPropertyPolicy
	 */
	private UIDomainExceptPropertyBean processExceptDomainObjectPropertyPolicy(
			ExceptDomainObjectPropertyPolicy exceptDomainObjectPropertyPolicy){
		UIDomainExceptPropertyBean uiDomainPropertyExepHolder = new UIDomainExceptPropertyBean();
		uiDomainPropertyExepHolder.setDomainObject(exceptDomainObjectPropertyPolicy.domainObject());
		uiDomainPropertyExepHolder.setPolicy(exceptDomainObjectPropertyPolicy.genralPolicy());
		uiDomainPropertyExepHolder.setPropertyList(processDomainProperties(exceptDomainObjectPropertyPolicy.properties()));
		return uiDomainPropertyExepHolder;
	}
	
	/**
	 * @return
	 * @param domainProperties
	 */
	private List<String> processDomainProperties(DomainProperties domainProperties[]){
		List<String> propertyList = new ArrayList<String>();
		for(DomainProperties domainPropertys : domainProperties ){
			propertyList.add(domainPropertys.property());
		}
		return propertyList;
	}
}